/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.admin.base;

import vip.dexian.common.Message;
import vip.dexian.common.custom.editor.LocalDateTimeEditor;
import vip.dexian.common.custom.editor.LocalTimeEditor;
import vip.dexian.common.custom.editor.StringEditor;
import vip.dexian.common.utils.SpringUtils;
import vip.dexian.core.base.validation.BaseValidator;
import vip.dexian.core.exception.ConstraintViolationException;
import vip.dexian.core.exception.ServiceValidationException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Set;

/**
 * Controller 基类
 *
 * @author 挺好的 2023年05月30日 14:17
 */
@Slf4j
public class BaseController extends BaseValidator {

    /**
     * 注册自定义编辑器
     *
     * @param binder
     */
    @InitBinder
    protected void initBinder (WebDataBinder binder) {
        // 字符串去空格
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
        // 日期
        binder.registerCustomEditor(LocalDateTime.class, new LocalDateTimeEditor(true));
        // 时间
        binder.registerCustomEditor(LocalTime.class, new LocalTimeEditor(true));
        // 密码
        binder.registerCustomEditor(String.class, "vip/dexian/password/encoder", new StringEditor(true));
    }


    /**
     * 数据类型绑定错误
     *
     * @param exception
     *         {@link BindException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (BindException.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.UNPROCESSABLE_ENTITY)
    public Message handleDataTypeBindException (Exception exception, HttpServletRequest request,
            HttpServletResponse response) {
        log.error("数据类型绑定错误", exception);
        return Message.error("DB002");
    }

    /**
     * 数据库相关错误
     *
     * @param exception
     *         {@link DataAccessException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (DataAccessException.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR)
    public Message handleDataAccessException (Exception exception, HttpServletRequest request,
            HttpServletResponse response) {

        log.error("数据库操作出错了", exception);

        return Message.error("DB001");
    }


    /**
     * 方法参数类型不一致异常
     *
     * @param exception
     *         {@link DataAccessException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (MethodArgumentTypeMismatchException.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.UNPROCESSABLE_ENTITY)
    public Message handleMethodArgumentTypeMismatchException (Exception exception, HttpServletRequest request,
            HttpServletResponse response) {

        log.error("方法参数类型不一致", exception);

        MethodArgumentTypeMismatchException methodArgumentTypeMismatchException = (MethodArgumentTypeMismatchException) exception;

        return Message.warn(methodArgumentTypeMismatchException.getMessage());
    }

    /**
     * 文件大小超过限制
     *
     * @param exception
     *         {@link MultipartException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (MultipartException.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.UNPROCESSABLE_ENTITY)
    public Message handleFormFileUploadException (Exception exception, HttpServletRequest request,
            HttpServletResponse response) {

        log.error("文件大小超过限制", exception);
        return Message.error("00005", null, 10);
    }

    /**
     * 表单校验错误消息，处理自定义ConstraintViolationException
     *
     * @param exception
     *         {@link ConstraintViolationException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (ConstraintViolationException.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.UNPROCESSABLE_ENTITY)
    public Message handleFormValidationException (Exception exception, HttpServletRequest request,
            HttpServletResponse response) {

        ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;

        log.debug("表单校验异常（Controller、Service）", constraintViolationException);
        return Message.warn(constraintViolationException.getMessage());
    }

    /**
     * 表单校验错误消息，处理jakarta ConstrainViolationException
     *
     * @param exception
     *         {@link jakarta.validation.ConstraintViolationException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (jakarta.validation.ConstraintViolationException.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.UNPROCESSABLE_ENTITY)
    public Message handleFormHibernateConstraintViolationException (Exception exception, HttpServletRequest request,
            HttpServletResponse response) {

        jakarta.validation.ConstraintViolationException constraintViolationException = (jakarta.validation.ConstraintViolationException) exception;

        Set <ConstraintViolation <?>> constraintViolations = constraintViolationException.getConstraintViolations();

        for (ConstraintViolation <?> constraintViolation : constraintViolations) {
            log.debug("表单校验异常（Controller、Service）", constraintViolationException);
            return Message.warn(constraintViolation.getMessage());
        }

        return Message.warn("数据格式校验错误");
    }

    /**
     * Service/Manager 校验异常
     *
     * @param exception
     *         {@link ServiceValidationException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (ServiceValidationException.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.BAD_REQUEST)
    public Message handleServiceValidationException (Exception exception, HttpServletRequest request,
            HttpServletResponse response) {

        ServiceValidationException serviceValidationException = (ServiceValidationException) exception;

        String message = serviceValidationException.getMessage();
        Object[] args = serviceValidationException.getArgs();

        log.debug("[{}]{}", message, SpringUtils.getMessage(message, args), serviceValidationException);

        return Message.warn(message, args);
    }


    /**
     * 其他Exception
     *
     * @param exception
     *         {@link DataAccessException}
     * @param request
     *         {@link jakarta.servlet.http.HttpServletRequest}
     * @param response
     *         {@link jakarta.servlet.http.HttpServletResponse}
     *
     * @return {@link Message}
     */
    @ExceptionHandler (Exception.class)
    @ResponseBody
    @ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR)
    public Message handleException (Exception exception, HttpServletRequest request, HttpServletResponse response) {

        log.error("全局错误拉", exception);

        return Message.error(500);
    }
}
